﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using DDotNet.Web.UI.AutomatedPageBuilder.Configuration;
using DDotNet.Web.UI.AutomatedPageBuilder.Enum;

namespace DDotNet.Web.UI.AutomatedPageBuilder
{
    internal sealed class OperationsFileRepositorium
    {
        #region Singleton Pattern Code

        /// <summary>
        /// Contains a Singleton Class Instance
        /// </summary>
        private static OperationsFileRepositorium _SingletonInstance = new OperationsFileRepositorium();

        /// <summary>
        /// Returna a Class Singleton Instance
        /// </summary>
        public static OperationsFileRepositorium Instance
        {
            get { return _SingletonInstance; }
        }

        /// <summary>
        /// Private Class Constructor
        /// </summary>
        private OperationsFileRepositorium()
        {
            this.RefreshOperationsList();
        }

        #endregion

        /// <summary>
        /// Reload Operations List
        /// </summary>
        private void RefreshOperationsList()
        {
            // Create Objects
            AutomatedPageBuilderSettings settings = AutomatedPageBuilderSettingsHandler.GetSetting();

            // Reset Operations Dictionary
            this._Operations = new Dictionary<string, string>();

            // Check If Directory Exists
            if (! Directory.Exists(settings.PhysicalPath))
            {
                throw (new DirectoryNotFoundException(
                    "Não foi possível acessar o diretório de arquivos de operação do CRUDPageBuilder."));
            }

            // Looping on Files
            foreach (string fileName in Directory.GetFiles(settings.PhysicalPath))
            {
                this.GetOperationFileInfo(fileName);
            }
        }

        /// <summary>
        /// Recupera as Definições de um Arquivo de Operações
        /// </summary>
        /// <param name="fileName">Nome do Arquivo</param>
        private void GetOperationFileInfo(string fileName)
        {
            // Create Objects
            XmlDocument xmlDocument;
            string cacheKey;
            
            // Load File Into XMLDocument
            xmlDocument = new XmlDocument();
            xmlDocument.Load(fileName);
            
            // Create Cache Key
            cacheKey = xmlDocument.SelectSingleNode("Operation/@entityType").InnerText + ":" +
            xmlDocument.SelectSingleNode("Operation/@operationType").InnerText;
            
            // Get Entity Type and Operation
            this._Operations.Add(cacheKey.ToUpper(), fileName);
        }

        private Dictionary<string, string> _Operations;

        /// <summary>
        /// Get a Definition for Operation Setting
        /// </summary>
        /// <param name="type"></param>
        /// <param name="operation"></param>
        public void GetTypeAndOperation(string type, CRUDOperation operation)
        {
            // Create Objects
            string cacheKey = type + ":" + operation.ToString();
            
            // Ensure Type and Operation On List
            this.EnsureTypeAndOperation(cacheKey.ToUpper(), operation, type);
        }

        /// <summary>
        /// Check If Operation and Key Exists on File List
        /// </summary>
        /// <param name="cacheKey"></param>
        /// <param name="operation"></param>
        /// <param name="type"></param>
        private void EnsureTypeAndOperation(string cacheKey, CRUDOperation operation, string type)
        {
            // Check If Definition Are Not on Cache
            if (! this._Operations.ContainsKey(cacheKey))
            {
                // Lock Operations
                lock (typeof (OperationsFileRepositorium))
                {
                    // Double Check Definition on Cache
                    if (!this._Operations.ContainsKey(cacheKey))
                    {
                        // Refresh Dictionary Definitions
                        this.RefreshOperationsList();
                    }

                    // Check Definitions Again (After Dictionary Refresh Operation)
                    if (!this._Operations.ContainsKey(cacheKey))
                    {
                        // Throw Not Found Exception
                        throw (new NotSupportedException(
                            string.Format(
                                "Não foi possível encontrar o arquivo de definição para a entidade \"{0}\" e operação \"{1}\"",
                                type, operation.ToString())));
                    }
                }
            }
        }
    }
}